<template>
    <canvas ref="starsCanvas" :class="cn('absolute inset-0 w-full h-full', $props.class)"></canvas>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { cn } from "@/lib/utils";

interface Star {
    x: number;
    y: number;
    z: number;
    speed: number;
}

const props = withDefaults(
    defineProps<{
        color?: string;
        count?: number;
        class?: string;
    }>(),
    {
        color: "#FFF",
        count: 200,
    },
);

const starsCanvas = ref<HTMLCanvasElement | null>(null);
let perspective: number = 0;
let stars: Star[] = [];
let ctx: CanvasRenderingContext2D | null = null;

onMounted(() => {
    const canvas = starsCanvas.value;
    if (!canvas) return;

    window.addEventListener("resize", resizeCanvas);
    resizeCanvas(); // Call it initially to set correct size

    perspective = canvas.width / 2;
    stars = [];

    // Initialize stars
    for (let i = 0; i < props.count; i++) {
        stars.push({
            x: (Math.random() - 0.5) * 2 * canvas.width,//随机生成 x 坐标，(Math.random() - 0.5) * 2 会生成 -1 到 1 之间的随机数，乘以 canvas.width 将范围扩展到整个画布宽度，这样星星会分布在画布的左右两侧
            y: (Math.random() - 0.5) * 2 * canvas.height,
            z: Math.random() * canvas.width,//z 坐标表示星星的深度
            speed: Math.random() * 5 + 2, // Speed for falling effect
        });
    }

    animate(); // Start animation
});

function hexToRgb() {
    let hex = props.color.replace(/^#/, "");

    // If the hex code is 3 characters, expand it to 6 characters
    if (hex.length === 3) {
        hex = hex
            .split("")
            .map((char) => char + char)
            .join("");
    }

    // Parse the r, g, b values from the hex string
    const bigint = parseInt(hex, 16);
    const r = (bigint >> 16) & 255; // Extract the red component
    const g = (bigint >> 8) & 255; // Extract the green component
    const b = bigint & 255; // Extract the blue component

    // Return the RGB values as a string separated by spaces
    return {
        r,
        g,
        b,
    };
}

// Function to draw a star with a sharp line and blurred trail
function drawStar(star: Star) {
    const canvas = starsCanvas.value;
    if (!canvas) return;

    ctx = canvas.getContext("2d");
    if (!ctx) return;

    const scale = perspective / (perspective + star.z); // 3D perspective scale
    const x2d = canvas.width / 2 + star.x * scale;
    const y2d = canvas.height / 2 + star.y * scale;
    const size = Math.max(scale * 3, 0.5); // Size based on perspective

    // Previous position for a trail effect 计算拖尾缩放比例
    const prevScale = perspective / (perspective + star.z + star.speed * 15); // Longer trail distance
    const xPrev = canvas.width / 2 + star.x * prevScale;
    const yPrev = canvas.height / 2 + star.y * prevScale;

    const rgb = hexToRgb();

    // Draw blurred trail (longer, with low opacity)
    ctx.save(); // Save current context state for restoring later
    ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`;
    ctx.lineWidth = size * 2.5; // Thicker trail for a blur effect
    ctx.shadowBlur = 35; // Add blur to the trail
    ctx.shadowColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`;
    ctx.beginPath();
    ctx.moveTo(x2d, y2d);
    ctx.lineTo(xPrev, yPrev); // Longer trail
    ctx.stroke();
    ctx.restore(); // Restore context state to remove blur from the main line

    // Draw sharp line (no blur)
    ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.6)`;
    ctx.lineWidth = size; // The line width is the same as the star's size
    ctx.beginPath();
    ctx.moveTo(x2d, y2d);
    ctx.lineTo(xPrev, yPrev); // Sharp trail
    ctx.stroke();

    // Draw the actual star (dot)
    ctx.fillStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1)`;
    ctx.beginPath();
    ctx.arc(x2d, y2d, size / 4, 0, Math.PI * 2); // Dot with size matching the width
    ctx.fill();
}

// Function to animate the stars
function animate() {
    const canvas = starsCanvas.value;
    if (!canvas) return;

    ctx = canvas.getContext("2d");
    if (!ctx) return;

    ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas for each frame

    stars.forEach((star) => {
        drawStar(star);

        // Move star towards the screen (decrease z)
        star.z -= star.speed;

        // Reset star when it reaches the viewer (z = 0)
        if (star.z <= 0) {
            star.z = canvas.width;
            star.x = (Math.random() - 0.5) * 2 * canvas.width;
            star.y = (Math.random() - 0.5) * 2 * canvas.height;
        }
    });

    requestAnimationFrame(animate); // Continue animation
}

// Set canvas to full screen
function resizeCanvas() {
    const canvas = starsCanvas.value;
    if (!canvas) return;

    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
}
</script>